/**
 * 
 */
package com.lnsystem.service.impl;

import java.math.BigInteger;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.ListIterator;

import org.apache.log4j.Logger;
import org.joda.time.DateTime;
import org.joda.time.LocalDate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import com.lnsystem.constants.LNSystemConstants;
import com.lnsystem.dao.AnalyticsEventDao;
import com.lnsystem.dao.StockDao;
import com.lnsystem.model.AnalyticsEvent;
import com.lnsystem.model.AnalyticsEventType;
import com.lnsystem.model.MovingAverage;
import com.lnsystem.model.Stock;
import com.lnsystem.model.StockDailyData;
import com.lnsystem.service.AnalyticsService;
import com.lnsystem.service.QuoteService;
import com.lnsystem.service.ReportingService;
import com.lnsystem.service.ScheduledTask;
import com.lnsystem.service.StockCheckService;
import com.lnsystem.service.SymbolsService;
import com.lnsystem.service.TechnicalIndicatorsService;
import com.lnsystem.test.StockServiceTest;

import org.springframework.transaction.annotation.Transactional;

/**
 * @author e5011835
 * 
 */
@Component
public class AnalyticsServiceImpl implements AnalyticsService {

	public static final int NUMBER_OF_WEEKS_FOR_BASE_FORMATION = 4;
	public static final double PRICE_CHANGE_PERCENTAGE_FOR_BASE_FORMATION = 2;

	Logger logger = Logger.getLogger(AnalyticsServiceImpl.class);

	@Autowired
	QuoteService quoteService;

	@Autowired
	SymbolsService symbolsService;

	@Autowired
	private ReportingService reportingService;

	@Autowired
	TechnicalIndicatorsService technicalIndicatorsService;
	
	@Autowired
	StockCheckService stockCheckService;

	@Autowired
	StockDao stockDao;
	
	@Autowired
	AnalyticsEventDao analyticsEventDao;

	// this will get stocks that had their price cross SMA today
	@Override
	public List<Stock> getStocksWithPriceCrossingSMA(int period) {
		long startTime = System.currentTimeMillis();
		List<Stock> result = new ArrayList<Stock>();

		// List<Stock> allStocks = stockDao.getAllStocks();
		// List<Stock> allStocks =
		// stockDao.getStocksOverCertainPriceVolume(5,1000000);
		List<Stock> allStocks = stockDao
				.getStocksByPriceRangeOverCertainVolume(1, 50, 500000);

		for (Stock s : allStocks) {

			if (isPriceCrossingSMAForStock(s, period)) {
				result.add(s);
			}
			// break;
		}
		long endTime = System.currentTimeMillis();
		logger.info("getStocksWithPriceCrossingSMA() took "
				+ (endTime - startTime) + " milliseconds");
		return result;
	}

	@Override
	public boolean isPriceCrossingSMAForStock(Stock s, int period) {

		List<StockDailyData> dailyDataList = stockDao.getStockDailyData(s,true);
		technicalIndicatorsService.calculateSMA(dailyDataList, period);

		BigInteger averageVolume = stockDao.getAverageVolume(s);

		if (dailyDataList != null && dailyDataList.size() > 1) {
			StockDailyData todaysData = dailyDataList.get(0);

			// TODO - might throw indexoutofbound exception here.. need to add
			// check
			StockDailyData yesterDaysData = dailyDataList.get(1);

			if (todaysData.getVolume().compareTo(averageVolume)>=0) { // volume surge
				double todaysSMA = stockDao.getSMA(todaysData, period);
				if (todaysSMA > 0 && todaysData.getClose() > 0
						&& yesterDaysData.getClose() > 0
						&& yesterDaysData.getClose() < todaysSMA
						&& todaysData.getClose() > todaysSMA) {// price crosses
																// sma
					logger.info(">>> Stock: " + s.getSymbol()
							+ " ---volume/avgVolume:" + todaysData.getVolume()
							+ "/" + averageVolume + " Price("
							+ todaysData.getClose() + ") crossing SMA-"
							+ period + "(" + todaysSMA + ") on "
							+ todaysData.getDate());
					return true;
				} else {
					// logger.info(">>> Stock: "+s.getSymbol()+" price : "+todaysData.getClose()+" ---volume/avgVolume:"+todaysData.getVolume()+"/"+averageVolume);
				}
			} else {
				// logger.info(">>> Stock: "+s.getSymbol()+" price : "+todaysData.getClose()+" ---volume/avgVolume:"+todaysData.getVolume()+"/"+averageVolume);
			}
		} else {
			// logger.info(">>> Stock: "+s.getSymbol()+" No daily data found. ");
		}
		return false;
	}

	@Override
	public List<Stock> getStocksWithSMACrossOvers(int period) {

		long startTime = System.currentTimeMillis();

		List<Stock> result = new ArrayList<Stock>();
		List<Stock> allStocks = stockDao.getAllStocks();

		for (Stock s : allStocks) {

			if (isSMACrossOverForStock(s)) {
				result.add(s);
			}
		}
		long endTime = System.currentTimeMillis();
		logger.info("getStocksWithSMACrossOvers() took "
				+ (endTime - startTime) + " milliseconds");
		return result;
	}

	@Override
	public boolean isSMACrossOverForStock(Stock s) {

		// TODO - to be implemented..
		List<StockDailyData> dailyDataList = s.getStockDailyData();
		if (dailyDataList != null && dailyDataList.size() > 0) {
			StockDailyData todaysData = dailyDataList
					.get(dailyDataList.size() - 1);

			if (todaysData.getVolume().compareTo(todaysData.getAverageVolume())>=0) {

			}
		}
		return false;
	}

	@Override
	@Transactional
	public List<Stock> getBasingStocks(LocalDate date) {

		logger.info(">>> getBasingStocks(DateTime date) starting to scan on or before: "
				+ date);

		long startTime = System.currentTimeMillis();
		List<Stock> result = new ArrayList<Stock>();

		// List<Stock> allStocks = stockDao.getAllStocks();
		List<Stock> allStocks = stockDao
				.getStocksByPriceRangeOverCertainVolume(10, 15, 500000);

		for (Stock s : allStocks) {

			if (isStockFormingBase(s, date)) {
				result.add(s);
			}
		}

		long endTime = System.currentTimeMillis();
		logger.info(">>> getBasingStocks(DateTime date) took: "
				+ (endTime - startTime) + " ms.");
		return result;
	}

	@Override
	public boolean isStockFormingBase(Stock s, LocalDate date) {

		LocalDate endDate = date;
		LocalDate startDate = date
				.minusWeeks(NUMBER_OF_WEEKS_FOR_BASE_FORMATION);
		logger.debug(">>> Checking base formation between ["
				+ startDate.toString() + "] and [" + endDate.toString()
				+ "]...");
		List<StockDailyData> dailyDataList = stockDao.getStockDailyData(s,
				startDate, endDate,true);

		if (dailyDataList != null && dailyDataList.size() > 0) {

			// for now we check close price of beginning day & last day to see
			// if it is a tight close.
			// TODO - this logic may need to be improved to detect
			// flat/(deep)cup/saucer bases,
			// and potentially whether it is a early/late stage base..

			double closePriceAtBeginning = dailyDataList.get(0).getClose();
			double closePriceAtEnd = dailyDataList
					.get(dailyDataList.size() - 1).getClose();

			if (Math.abs(100 - (closePriceAtBeginning / closePriceAtEnd * 100)) <= PRICE_CHANGE_PERCENTAGE_FOR_BASE_FORMATION) { // checking
																																	// if
																																	// the
																																	// close
																																	// is
																																	// within
																																	// a
																																	// certain
																																	// percentage
																																	// as
																																	// defined
																																	// by
																																	// PRICE_CHANGE_PERCENTAGE_FOR_BASE_FORMATION
				logger.info(">>> "
						+ s.getSymbol()
						+ " detected to be forming base... range/%: ["
						+ closePriceAtBeginning
						+ "-"
						+ closePriceAtEnd
						+ "/"
						+ Math.abs(100 - (closePriceAtBeginning
								/ closePriceAtEnd * 100)) + " ]  between ["
						+ startDate.toString() + "] " + "and ["
						+ endDate.toString() + "]...");
				return true;
			}
		}

		return false;
	}

	@Override
	@Transactional
	public boolean isStockFormingBase(String symbol, LocalDate date) {

		Stock s = stockDao.getStockBySymbol(symbol);
		LocalDate endDate = date;
		LocalDate startDate = date
				.minusWeeks(NUMBER_OF_WEEKS_FOR_BASE_FORMATION);
		logger.debug(">>> Checking base formation between ["
				+ startDate.toString() + "] and [" + endDate.toString()
				+ "]...");
		List<StockDailyData> dailyDataList = stockDao.getStockDailyData(s,
				startDate, endDate,true);

		if (dailyDataList != null && dailyDataList.size() > 0) {

			// for now we check close price of beginning day & last day to see
			// if it is a tight close.
			// TODO - this logic may need to be improved to detect
			// flat/(deep)cup/saucer bases,
			// and potentially whether it is a early/late stage base..

			double closePriceAtBeginning = dailyDataList.get(0).getClose();
			double closePriceAtEnd = dailyDataList
					.get(dailyDataList.size() - 1).getClose();

			if (Math.abs(100 - (closePriceAtBeginning / closePriceAtEnd * 100)) <= PRICE_CHANGE_PERCENTAGE_FOR_BASE_FORMATION) { // checking
																																	// if
																																	// the
																																	// close
																																	// is
																																	// within
																																	// a
																																	// certain
																																	// percentage
																																	// as
																																	// defined
																																	// by
																																	// PRICE_CHANGE_PERCENTAGE_FOR_BASE_FORMATION
				logger.info(">>> "
						+ symbol
						+ " detected to be forming base... range/%: ["
						+ closePriceAtBeginning
						+ "-"
						+ closePriceAtEnd
						+ "/"
						+ Math.abs(100 - (closePriceAtBeginning
								/ closePriceAtEnd * 100)) + " ]  between ["
						+ startDate.toString() + "] " + "and ["
						+ endDate.toString() + "]...");
				return true;
			}
		}

		return false;
	}

	@Override
	public List<Stock> getStocksWithConvergingSMA() {
		// TODO Auto-generated method stub

		return null;
	}

	@Override
	public boolean isSMAConvergingForStock(Stock s) {
		// TODO Auto-generated method stub
		return false;
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	@Transactional
	public List<Stock> getStocksWithMostRecentPocketPivots(
			double minStockPrice, double maxStockPrice, double minVolume,
			int smaPeriod, LocalDate scanStartDate,
			boolean currentMonthResultsOnly, boolean basingStocksOnly) {
		long start1 = System.currentTimeMillis();

		if (maxStockPrice == 0) {
			maxStockPrice = 9999;
		}
		if (minVolume == 0) {
			minVolume = 10000;
		}

		List<Stock> stockList = stockDao
				.getStocksByPriceRangeOverCertainVolume(minStockPrice,
						maxStockPrice, minVolume);
		
		
		List<Stock> result = getStocksWithMostRecentPocketPivots(stockList,smaPeriod,scanStartDate,currentMonthResultsOnly,basingStocksOnly);
		
		//check stock's health
		stockCheckService.checkStock(result);
		
		reportingService.generateReportOfStockPicks(
				"StocksWithMostRecentPocketPivots",
				"StocksWithMostRecentPocketPivots from " + scanStartDate
						+ ", CurrentMonthOnly=" + currentMonthResultsOnly
						+ ", basingOnly=" + basingStocksOnly
						+ ", pricerange/vol=[" + minStockPrice + "-"
						+ maxStockPrice + "," + minVolume + "]", result);
		return result;
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	@Transactional
	public List<Stock> getStocksWithMostRecentPocketPivots(
			List<Stock> stockList,
			int smaPeriod, LocalDate scanStartDate,
			boolean currentMonthResultsOnly, boolean basingStocksOnly) {
		
		long start1 = System.currentTimeMillis();


		List<Stock> result = new ArrayList<Stock>();

		for (Stock s : stockList) {

			List<StockDailyData> pocketPivots = new ArrayList<StockDailyData>();

			StockDailyData pocketPivot = getMostRecentPocketPivot(s, smaPeriod,
					currentMonthResultsOnly, scanStartDate);

			if (pocketPivot != null) {

				// check for base formation here..
				if (isStockFormingBase(s,
						new LocalDate(pocketPivot.getDate()).minusDays(1))) {
					logger.info("!!!!*****!!!****** Symbol: " + s.getSymbol()
							+ " also formed base before pocket pivot on "
							+ pocketPivot.getDate());

					if (pocketPivot.getNotes() == null) {
						pocketPivot.setNotes("");
					}
					
					pocketPivot.setNotes(pocketPivot.getNotes()+ "; Formed base before pocket pivot on "+ pocketPivot.getDate());
					s.setNotes(s.getNotes()+ "; Formed base before pocket pivot on "+ pocketPivot.getDate());
					
					pocketPivots.add(pocketPivot);
					s.setPocketPivots(pocketPivots); // this line may not be
														// needed..
					result.add(s);

				} else {

					if (!basingStocksOnly) { // false - so report pocket pivots
												// without base formation as
												// well...

						if (pocketPivot.getNotes() == null) {
							pocketPivot.setNotes("");
						}
						
						pocketPivot.setNotes(pocketPivot.getNotes()+ "; Pocket pivot found on "+ pocketPivot.getDate());
						s.setNotes(pocketPivot.getNotes()+ "; Pocket pivot found on "+ pocketPivot.getDate());
						
						pocketPivots.add(pocketPivot);
						s.setPocketPivots(pocketPivots); // this line may not be
															// needed..
						result.add(s);
					}
				}
			}
			long end1 = System.currentTimeMillis();
			logger.debug("Time taken by getStocksWithMostRecentPocketPivots : "
					+ (end1 - start1) + " ms");

		}

		return result;
	}
	
	/**
	 * {@inheritDoc}
	 */
	@Override
	@Transactional
	public boolean isPocketPivotFound(Stock s, 
			List<StockDailyData> dailyDataList, int i, int smaPeriod, LocalDate scanStartDate) {
		
		double todaysSMAValue=0;
		try {
			// default to current date if scanStartDate is unspecified.
			if(scanStartDate == null) {
				scanStartDate = LocalDate.now();
			}
			
			// get the dailydatalist if it is null.
			if(dailyDataList == null) {
				dailyDataList = stockDao
						.getStockDailyDataBeforeDate(s, scanStartDate,true);
			}
			if(dailyDataList!=null && dailyDataList.size()>i) {
				StockDailyData todaysData = dailyDataList.get(i);
				
				//double averageVolume = stockDao.getAverageVolume(s,LNSystemConstants.DAYS_FOR_AVGVOL_CALCULATION);
				BigInteger maxDownDayVolume = stockDao.getMaxDownDayVolume(todaysData);
							
				// calculate moving average if not done yet..
				if(todaysData.getMovingAverages() != null && todaysData.getMovingAverages().size()==0) {
					technicalIndicatorsService.calculateSMA(dailyDataList, smaPeriod);
				}
				
				MovingAverage ma = todaysData.getMovingAverage(0, smaPeriod);
				if(ma!=null) {
					todaysSMAValue = todaysData.getMovingAverage(0, smaPeriod).getMaValue();
				} else {
					logger.debug("todaysData.getMovingAverage() is null");
				}
				
				if(
						todaysData.getPriceAction().equals("G") //up day
						&& ((todaysData.getLow() <= todaysSMAValue) 
								&& (todaysData.getClose() >= todaysSMAValue)) // today's price action has crossed SMA
						&& todaysData.getVolume().compareTo(maxDownDayVolume)>=0 //average or greater than average volume
				) {
					logger.info(">>>from new isPocketPivotFound()>>>>   Symbol: "
							+ s.getSymbol()
							+ " -- Pocket pivot found on "
							+ todaysData.getDate());
					
					s.setNotes(" -- Pocket pivot found on ["+todaysData.getDate()+"]--");
					
					
					addAnalyticsEvent(s,todaysData, "POCKET_PIVOT");
					
/*					// add an entry into analyticsevent table.
					//first get existing pocket pivot event (if any) for the stockdaily and add to details field.
					AnalyticsEvent event = analyticsEventDao.getAnalyticsEventsByTypeForADailyData(todaysData,"POCKET_PIVOT");
					
					if(event == null) {
						event = new AnalyticsEvent();
					} else {
						logger.info(">>> AnalyticsEvent table already has a POCKET_PIVOT entry for :"+s.getSymbol()+" on ["+todaysData.getDate()+"]");
					}		
					
					StringBuilder sBuilder = new StringBuilder("");
					sBuilder.append(" -- Pocket pivot found on ["+todaysData.getDate()+"]--");
					//calculate pivots and add more details based on current price vs pivots
					// also set rank based on the pivots.
					
					event.setEventType("POCKET_PIVOT");
					event.setDetails(sBuilder.toString());
					event.setSymbol(s.getSymbol());
					event.setCreatedOn(LocalDate.now().toDate());
					
					event.setStockId(s);
					event.setStockDailyId(todaysData);
					
					
					PricePositionToPivots pricePositionToPivots = 
							getPricePositionToPivots(s, LNSystemConstants.FIBNOCCI_PIVOT, LNSystemConstants.DAILY_TIMEFRAME);
					
					event.setPricePivotPosition(pricePositionToPivots.getPricePosition());
					event.setRisk(pricePositionToPivots.getRisk());
					event.setReward(pricePositionToPivots.getReward());
					event.setRiskReward(pricePositionToPivots.getRiskRewardRatio());
					
					event.setPriceOnEvent(todaysData.getClose());
					
					//persist event information.
					analyticsEventDao.saveAnalyticsEvent(event);*/
		
				return true;
				}			
			}


		} catch(Exception e) {
			e.printStackTrace();
		}
		return false;
	}


	/**
	 * {@inheritDoc}
	 */
	//@Override
	@Transactional
	public boolean isPocketPivotFound_old(Stock s, 
			List<StockDailyData> dailyDataList, int i, int smaPeriod, LocalDate scanStartDate) {
		
		long start2;
		long end2;
		long start1 = 0;
		long end1;
		long totalGetSMATime = 0;
		double todaysSMAValue=0;
		
		
		// default to current date if scanStartDate is unspecified.
		if(scanStartDate == null) {
			scanStartDate = LocalDate.now();
		}
		
		// get the dailydatalist if it is null.
		if(dailyDataList == null) {
			dailyDataList = stockDao
					.getStockDailyDataBeforeDate(s, scanStartDate,true);
		}
		if(dailyDataList!=null && dailyDataList.size()>i) {
			StockDailyData todaysData = dailyDataList.get(i);
			
			BigInteger averageVolume = stockDao.getAverageVolume(s);

			start2 = System.currentTimeMillis();
			
			// calculate moving average if not done yet..
			if(todaysData.getMovingAverages() != null && todaysData.getMovingAverages().size()==0) {
				technicalIndicatorsService.calculateSMA(dailyDataList, smaPeriod);
			}
			MovingAverage ma = todaysData.getMovingAverage(0, smaPeriod);
			if(ma!=null) {
				todaysSMAValue = todaysData.getMovingAverage(0, smaPeriod).getMaValue();
			} else {
				logger.debug("todaysData.getMovingAverage() is null");
			}
			
			
			
			end2 = System.currentTimeMillis();
			logger.debug(" >>stockDao.getSMA(todaysData, smaPeriod) took : "
					+ (end2 - start2) + " ms");
			totalGetSMATime += (end2 - start2);

			if ((i + 1) < dailyDataList.size()) { // making sure if there
													// are elements in the
													// list before get().
				StockDailyData yesterDaysData = dailyDataList.get(i + 1);

				LocalDate temp1 = new LocalDate(todaysData.getDate());
				LocalDate temp2 = new LocalDate(yesterDaysData.getDate());
				logger.info(">>>>> "+s.getSymbol()+", today[date=" + temp1 + "close="
						+ todaysData.getClose() + ",volume="
						+ todaysData.getVolume() + ",avg.volume="
						+ averageVolume + "smaVal=" + todaysSMAValue
						+ "], yesterday[date=" + temp2 + ",close="
						+ yesterDaysData.getClose() + ",volume="
						+ yesterDaysData.getVolume() + ",avg.volume="
						+ averageVolume + "smaVal=" + todaysSMAValue + "]");

				if (yesterDaysData != null && todaysSMAValue >0
						&& yesterDaysData.getClose() < todaysSMAValue // price
																		// was
																		// below
																		// sma
																		// yesterday.
						&& todaysData.getClose() > yesterDaysData
								.getClose() // up day
						&& todaysData.getVolume().compareTo(averageVolume)>=0 // heavy volume
						&& todaysData.getClose() > todaysSMAValue // price
																	// crosses
																	// SMA
				) {

					// 10dma pocket pivot...
					// check down day volume in the last 10 trading days.
					if (isDownDaysVolumeOk(todaysData, dailyDataList, i + 1)) {

						end1 = System.currentTimeMillis();
						logger.info(">> Symbol: "
								+ s.getSymbol()
								+ " -- Pocket pivot found on "
								+ todaysData.getDate()
								+ ".. Time taken by getMostRecentPocketPivot : "
								+ (end1 - start1) + " ms, totalGetSMATime="
								+ totalGetSMATime);

						logger.debug("getMostRecentPocketPivot found PP... Time taken by getMostRecentPocketPivot : "
								+ (end1 - start1)
								+ " ms, totalGetSMATime="
								+ totalGetSMATime);
						return true;
					} else {
						logger.info(">> downVolume for the last 10 days not OK");
						return false;
					}
				}
				// }
			}
		}
		return false;
	}
	
	/**
	 * {@inheritDoc}
	 */
	@Override
	@Transactional
	public List<Stock> getStocksShowingPocketPivotToday(
			double minStockPrice, double maxStockPrice, double minVolume,
			int smaPeriod, LocalDate scanStartDate,boolean basingStocksOnly) {
		
		List<Stock> result = new ArrayList<Stock>();
		
		List<Stock> stockList = stockDao
				.getStocksByPriceRangeOverCertainVolume(minStockPrice,
						maxStockPrice, minVolume);
		
		for(Stock stock : stockList) {
			
			if(isPocketPivotFound(stock, null, 0, smaPeriod, scanStartDate)) {
				
/*				
 				if(scanStartDate == null) {
					stock.setNotes(stock.getNotes()+"; pocket pivot found on :"+LocalDate.now());
				} else {
					stock.setNotes(stock.getNotes()+"; pocket pivot found on :"+scanStartDate);
				}
*/
				result.add(stock);
			}
			
		}
		
		
		//check stock's health
		stockCheckService.checkStock(result);
		
		//add the stocks showing pocket pivot to analyticsevent table..
		
		
		
		reportingService.generateReportOfStockPicks(
				"StocksShowingPocketPivotToday",
				"Stocks showing PocketPivot Today [" + scanStartDate
						+ "]"
						+ ", basingOnly=" + basingStocksOnly
						+ ", pricerange/vol=[" + minStockPrice + "-"
						+ maxStockPrice + "," + minVolume + "]", result);
		return result;
		
		
	}
	
	
	/**
	 * {@inheritDoc}
	 */
	@Override
	@Transactional
	public StockDailyData getMostRecentPocketPivot(Stock s, int smaPeriod,
			boolean currentMonthResultsOnly, LocalDate scanStartDate) {
		BigInteger averageVolume;
		double todaysSMAValue;
		StockDailyData yesterDaysData = null;
		// int iteratorIndex = 0;
		int currentMonth;

		// temp variables
		long start2;
		long end2;
		long start1;
		long end1;
		long totalGetSMATime = 0;

		start1 = System.currentTimeMillis();
		// List<StockDailyData> dailyDataList = stockDao.getStockDailyData(s);
		List<StockDailyData> dailyDataList = stockDao
				.getStockDailyDataBeforeDate(s, scanStartDate,true);
		technicalIndicatorsService.calculateSMA(dailyDataList, smaPeriod);

		end1 = System.currentTimeMillis();
		logger.debug("dailyDataList.size="
				+ dailyDataList.size()
				+ ".. Time taken by stockDao.getStockDailyDataBeforeDate(s, scanStartDate) : "
				+ (end1 - start1) + " ms");
		
		

		// long start1 = System.currentTimeMillis();
		//averageVolume = stockDao.getAverageVolume(s);
		averageVolume = stockDao.getAverageVolume(s,LNSystemConstants.DAYS_FOR_AVGVOL_CALCULATION);
		// long end1 = System.currentTimeMillis();
		// logger.info("Time taken by stockDao.getAverageVolume(s) : "+(end1-start1)+" ms");

		// ///////////start1 = System.currentTimeMillis();
		if (dailyDataList != null && dailyDataList.size() > 0) {

			
			// iteratorIndex = dailyDataList.size();

			int scanDateMonth = scanStartDate.getMonthOfYear();
			if (scanDateMonth == 1) {
				currentMonth = 12;
			} else {
				currentMonth = scanDateMonth;
			}

			// logger.info(">>> Scan date month: "+scanDateMonth+", currentMonth:"+currentMonth);
			// ////////////start1 = System.currentTimeMillis();
			for (int i = 0; i < dailyDataList.size(); i++) {

				StockDailyData todaysData = dailyDataList.get(i);

				DateTime currentDailyDataDate = new DateTime(
						todaysData.getDate());
				logger.debug(">>> Scan date month: " + scanDateMonth
						+ ", currentMonth:" + currentMonth
						+ ", currentDailyDataDate:" + currentDailyDataDate);

				if (currentMonthResultsOnly
						&& currentMonth != currentDailyDataDate
								.getMonthOfYear()) {
					// logger.info(">>!!!!!!!! Symbol: "+ s.getSymbol()+
					// " --no pocket pivots found for current month : "+today.getMonthOfYear()+"/"+today.getYear());
					end1 = System.currentTimeMillis();
					logger.debug("No pivots for this month.. getMostRecentPocketPivot took : "
							+ (end1 - start1)
							+ " ms, totalGetSMATime="
							+ totalGetSMATime);
					return null;
				}

				 if(isPocketPivotFound(s, dailyDataList, i, smaPeriod, scanStartDate)) {
					 return todaysData;
				 } else {
					//i++; // this is to skip processing yesterday's data
					// again in case isDownDaysVolumeOk()
					// returns false
					// as we know already that the close<SMA
					//logger.debug(">> downVolume for the last 10 days not OK");
				 }

				
	/*			start2 = System.currentTimeMillis();
				// todaysSMAValue = stockDao.getSMA(todaysData, smaPeriod);
				todaysSMAValue = todaysData.getMovingAverage(0, smaPeriod)
						.getMaValue();
				end2 = System.currentTimeMillis();
				logger.debug(" >>stockDao.getSMA(todaysData, smaPeriod) took : "
						+ (end2 - start2) + " ms");
				totalGetSMATime += (end2 - start2);


				if ((i + 1) < dailyDataList.size()) { // making sure if there
														// are elements in the
														// list before get().
					yesterDaysData = dailyDataList.get(i + 1);

					LocalDate temp1 = new LocalDate(todaysData.getDate());
					LocalDate temp2 = new LocalDate(yesterDaysData.getDate());
					logger.debug(">>>>> today[date=" + temp1 + "close="
							+ todaysData.getClose() + ",volume="
							+ todaysData.getVolume() + ",avg.volume="
							+ averageVolume + "smaVal=" + todaysSMAValue
							+ "], yesterday[date=" + temp2 + ",close="
							+ yesterDaysData.getClose() + ",volume="
							+ yesterDaysData.getVolume() + ",avg.volume="
							+ averageVolume + "smaVal=" + todaysSMAValue + "]");

					if (yesterDaysData != null
							&& yesterDaysData.getClose() < todaysSMAValue // price
																			// was
																			// below
																			// sma
																			// yesterday.
							&& todaysData.getClose() > yesterDaysData
									.getClose() // up day
							&& todaysData.getVolume() > averageVolume // heavy
																		// volume
							&& todaysData.getClose() > todaysSMAValue // price
																		// crosses
																		// SMA
					) {

						// 10dma pocket pivot...
						// check down day volume in the last 10 trading days.
						if (isDownDaysVolumeOk(todaysData, dailyDataList, i + 1)) {

							end1 = System.currentTimeMillis();
							logger.info(">> Symbol: "
									+ s.getSymbol()
									+ " -- Pocket pivot found on "
									+ todaysData.getDate()
									+ ".. Time taken by getMostRecentPocketPivot : "
									+ (end1 - start1) + " ms, totalGetSMATime="
									+ totalGetSMATime);

							logger.debug("getMostRecentPocketPivot found PP... Time taken by getMostRecentPocketPivot : "
									+ (end1 - start1)
									+ " ms, totalGetSMATime="
									+ totalGetSMATime);
							return todaysData;
						} else {
							// logger.info(">>>>>>>>>>>>>>>> Symbol: "+
							// s.getSymbol()+
							// " -- Down day volume not OK. Invalid Potential Pocket pivot found on "+
							// todaysData.getDate());
							i++; // this is to skip processing yesterday's data
									// again in case isDownDaysVolumeOk()
									// returns false
									// as we know already that the close<SMA
							logger.debug(">> downVolume for the last 10 days not OK");
						}
					}
					// }
				}*/

			}
		}
		end1 = System.currentTimeMillis();
		logger.debug("getMostRecentPocketPivot() About to return null.. Time taken by getMostRecentPocketPivot : "
				+ (end1 - start1) + " ms, totalGetSMATime=" + totalGetSMATime);
		return null;
	}

	
	
	private boolean isDownDaysVolumeOk(StockDailyData todaysData,
			List<StockDailyData> dailyDataList, int iteratorIndex) {

		// ListIterator<StockDailyData> li =
		// dailyDataList.listIterator(iteratorIndex);

		logger.debug(">>>>>>Getting ready to compare todays data: [date="
				+ todaysData.getDate() + ", volume:" + todaysData.getVolume()
				+ "]");

		for (int i = 0; i < 10; i++) {
			// if (li.hasPrevious()) {

			if (iteratorIndex < dailyDataList.size()) { // make sure there are
														// elements in the
														// list..
				StockDailyData yesterDaysData = dailyDataList
						.get(iteratorIndex++);
				if (yesterDaysData != null) {

					if (iteratorIndex < dailyDataList.size()) {
						StockDailyData dayBeforeYesterdayData = dailyDataList
								.get(iteratorIndex);
						logger.debug(">>>>>>Previous days data: [date="
								+ yesterDaysData.getDate() + ", prev-open:"
								+ yesterDaysData.getOpen() + ",prev-close:"
								+ yesterDaysData.getClose()
								+ ", prevDay's volume:"
								+ yesterDaysData.getVolume() + "]");
						if (dayBeforeYesterdayData != null) {
							if ((yesterDaysData.getClose() < dayBeforeYesterdayData
									.getClose()) // down day
									&& yesterDaysData.getVolume().compareTo(todaysData.getVolume())>=0 // down day volume > up
															// day, so it does
															// not qualify as
															// pocket pivot day.
							) {
								return false;
							}
						} else {
							return false;
						}
					} else {
						return false;
					}
				} else {
					return false;
				}
			} else {
				return false;
			}

		}
		return true;

	}

	@Override
	public List<StockDailyData> getAllPocketPivots(Stock s, int smaPeriod) {
		BigInteger averageVolume;
		double todaysSMAValue;
		int iteratorIndex = 0;

		List<StockDailyData> allPocketPivots = new ArrayList<StockDailyData>();

		StockDailyData yesterDaysData = null;

		
		List<StockDailyData> dailyDataList = stockDao.getStockDailyData(s,true); //TODO - this method now returns records in desc order. Logic needs to be changed.
		averageVolume = stockDao.getAverageVolume(s);

		if (dailyDataList != null && dailyDataList.size() > 0) {

			iteratorIndex = dailyDataList.size();
			ListIterator<StockDailyData> li = dailyDataList
					.listIterator(iteratorIndex);

			while (li.hasPrevious()) {

				StockDailyData todaysData = (StockDailyData) li.previous();
				iteratorIndex--;
				if (li.hasPrevious()) {
					yesterDaysData = (StockDailyData) li.previous();
					iteratorIndex--;
				}

				todaysSMAValue = stockDao.getSMA(todaysData, smaPeriod);

				if (todaysData.getClose() > todaysData.getOpen() && // up day
						todaysData.getVolume().compareTo(averageVolume)>=0) { // heavy
																	// volume

					if (todaysData.getClose() > todaysSMAValue
							&& yesterDaysData != null
							&& yesterDaysData.getClose() < todaysSMAValue) {
						// 10dma pocket pivot...
						logger.info(">>>>>>>>>>>>>>>> Symbol: " + s.getSymbol()
								+ " -- Pocket pivot found on "
								+ todaysData.getDate());
						allPocketPivots.add(todaysData);
					} else {
						li = dailyDataList.listIterator(iteratorIndex); // reset
																		// iterator
																		// back
																		// by an
																		// element..
					}
				}
			}
		}
		return allPocketPivots;
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	@Transactional
	public List<Stock> getStocksWithSMACrossingAbove(double minStockPrice,
			double maxStockPrice, double minVolume, int lowerSMA,
			int higherSMA, boolean currentMonthResultsOnly,
			LocalDate scanStartDate) {

		List<Stock> result = new ArrayList<Stock>();
		List<Stock> stockList = stockDao
				.getStocksByPriceRangeOverCertainVolume(minStockPrice,
						maxStockPrice, minVolume);

		for (Stock s : stockList) {
			if (isSMACrossingAboveForStock(s, lowerSMA, higherSMA,
					currentMonthResultsOnly, scanStartDate)) {
				result.add(s);
			}
		}
		return result;
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public boolean isSMACrossingAboveForStock(Stock s, int lowerSMA,
			int higherSMA, boolean currentMonthResultsOnly,
			LocalDate scanStartDate) {
		int iteratorIndex = 0;
		List<StockDailyData> smaCrossList = new ArrayList<StockDailyData>();
		List<StockDailyData> dailyDataList = stockDao
				.getStockDailyDataBeforeDate(s, scanStartDate,true);
		iteratorIndex = dailyDataList.size();
		ListIterator<StockDailyData> li = dailyDataList
				.listIterator(iteratorIndex);

		while (li.hasPrevious()) {
			StockDailyData todaysData = (StockDailyData) li.previous();
			iteratorIndex--;

			LocalDate currentDailyDataDate = new LocalDate(todaysData.getDate());
			int currentMonth = scanStartDate.getDayOfMonth() == 1 ? (scanStartDate
					.getMonthOfYear() - 1) : scanStartDate.getMonthOfYear();
			if (currentMonthResultsOnly
					&& currentMonth != currentDailyDataDate.getMonthOfYear()) {
				// logger.info(">>!!!!!!!! Symbol: "+ s.getSymbol()+
				// " --no pocket pivots found for current month : "+today.getMonthOfYear()+"/"+today.getYear());
				return false;
			}

			MovingAverage todaysLowerSMA = todaysData.getMovingAverage(0,
					lowerSMA);
			// MovingAverage todaysHigherSMA = todaysData.getMovingAverage(0,
			// higherSMA);

			if (li.hasPrevious()) {
				StockDailyData yesterDaysData = (StockDailyData) li.previous();
				iteratorIndex--;
				MovingAverage yesterDaysLowerSMA = yesterDaysData
						.getMovingAverage(0, lowerSMA);
				MovingAverage yesterDaysHigherSMA = yesterDaysData
						.getMovingAverage(0, higherSMA);

				if (todaysLowerSMA.getMaValue() >= yesterDaysLowerSMA
						.getMaValue()
						&& yesterDaysLowerSMA.getMaValue() <= yesterDaysHigherSMA
								.getMaValue()
						&& todaysLowerSMA.getMaValue() >= yesterDaysHigherSMA
								.getMaValue()) {

					logger.info(">>> Symbol: " + s.getSymbol()
							+ " - Detected SMA" + lowerSMA
							+ " crossing above SMA" + higherSMA + " on ["
							+ todaysData.getDate() + "]");
					smaCrossList.add(todaysData);
					s.setSmCross(smaCrossList);
					return true;
				}
			}
		}

		return false;
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public List<Stock> getStocksWithSMACrossingBelow(double minStockPrice,
			double maxStockPrice, double minVolume, int lowerSMA,
			int higherSMA, boolean currentMonthResultsOnly,
			LocalDate scanStartDate) {
		// TODO Auto-generated method stub
		return null;
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public boolean isSMACrossingBelowForStock(Stock s, int lowerSMA,
			int higherSMA, boolean currentMonthResultsOnly,
			LocalDate scanStartDate) {
		// TODO Auto-generated method stub
		return false;
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	@Transactional
	public List<Stock> getStocksFormingBaseAndSMACrossingAbove(
			double minStockPrice, double maxStockPrice, double minVolume,
			int lowerSMA, int higherSMA, boolean currentMonthResultsOnly,
			LocalDate scanStartDate) {
		StockDailyData smaCross;
		List<Stock> result = new ArrayList<Stock>();
		List<Stock> smaCrossingStocksList = getStocksWithSMACrossingAbove(
				minStockPrice, maxStockPrice, minVolume, lowerSMA, higherSMA,
				currentMonthResultsOnly, scanStartDate);

		for (Stock s : smaCrossingStocksList) {
			List<StockDailyData> smaCrossList = s.getSmCross();
			if (smaCrossList != null && smaCrossList.size() > 0) {
				smaCross = smaCrossList.get(0);
				if (isStockFormingBase(s, new LocalDate(smaCross.getDate()))) { // pass
																				// the
																				// smacross
																				// date
																				// to
																				// check
																				// for
																				// base
																				// formation
																				// before
																				// that
																				// date.
					
					smaCross.setNotes(smaCross.getNotes() + ";" + lowerSMA+ "SMA" + " crossing above " + higherSMA+ "SMA on [" + smaCross.getDate()+ "] and stock forming base.");
					s.setNotes(s.getNotes() + ";" + lowerSMA+ "SMA" + " crossing above " + higherSMA+ "SMA on [" + smaCross.getDate()+ "] and stock forming base.");
					
					result.add(s);
				}
			}
		}

		reportingService.generateReportOfStockPicks(
				"StocksFormingBaseAndSMACrossingAbove",
				"StocksFormingBaseAndSMACrossingAbove from " + scanStartDate
						+ ", CurrentMonthOnly=" + currentMonthResultsOnly
						+ ", pricerange/vol=[" + minStockPrice + "-"
						+ maxStockPrice + "," + minVolume + "]", result);
		return result;

		/*
		 * List<Stock> smaCrossingStocksList =
		 * getStocksWithSMACrossingAbove(minStockPrice, maxStockPrice,
		 * minVolume, lowerSMA, higherSMA, currentMonthResultsOnly,
		 * scanStartDate);
		 * 
		 * 
		 * 
		 * for(Stock s : smaCrossingStocksList){
		 * logger.info(">>> Checking "+s.getSymbol()+" for base formation...");
		 * if(isSMACrossingAboveForStock(s, lowerSMA, higherSMA,
		 * currentMonthResultsOnly, scanStartDate)){ result.add(s); } }
		 * 
		 * return result;
		 */

	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	@Transactional
	public boolean isStockFormingBaseAndSMACrossingAbove(String symbol,
			int lowerSMA, int higherSMA, boolean currentMonthResultsOnly,
			LocalDate scanStartDate) {

		Stock s = stockDao.getStockBySymbol(symbol);
		boolean isStockFormingBase = isStockFormingBase(s, new LocalDate(
				"2014-05-19"));
		logger.info(">>>> forming base: " + isStockFormingBase);
		isSMACrossingAboveForStock(s, lowerSMA, higherSMA,
				currentMonthResultsOnly, scanStartDate);

		return false;
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	@Transactional
	public List<Stock> getStocksWithPriceAdvance(double percentageAdvance,
			LocalDate from, LocalDate to, double minStockPrice,
			double maxStockPrice, double minVolume) {
		
		int i = 1;
		List<Stock> result = new ArrayList<Stock>();

		List<Stock> stockList = stockDao
				.getStocksByPriceRangeOverCertainVolume(minStockPrice,
						maxStockPrice, minVolume);

		for (Stock s : stockList) {

			List<StockDailyData> dailyDataList = stockDao.getStockDailyData(s,
					from, to, true);

			if (dailyDataList != null && dailyDataList.size() > 1) {

				StockDailyData first = dailyDataList.get(0);
				StockDailyData last = dailyDataList
						.get(dailyDataList.size() - 1);
				double actualPercentageAdvance;

				actualPercentageAdvance = Math.ceil((first.getClose() - last
						.getClose()) / last.getClose() * 100);

				if (actualPercentageAdvance >= percentageAdvance) {
					logger.info(">>> " + s.getSymbol() + " advanced "
							+ actualPercentageAdvance + "(last:"
							+ last.getClose() + ", first:" + first.getClose()
							+ ") between [" + from + " and " + to + "]");
					
					s.setNotes(s.getNotes()+"; Advanced "+actualPercentageAdvance+"% from "+from+" to "+to);
					result.add(s);
				} else {
					// logger.info(""+s.getSymbol()+" advanced "+actualPercentageAdvance+"(last:"+last.getClose()+", first:"+first.getClose()+") between ["+from+" and "+to+"]");
				}
			}
		}
		
		//check stock's health
		stockCheckService.checkStock(result);
		
		getStocksWithMostRecentPocketPivots(result,10,LocalDate.now(),true,false);
		
		reportingService.generateReportOfStockPicks("StocksWithPriceAdvance",
				"StocksWitPriceAdvance [" + percentageAdvance + "% from "
						+ from + " to " + to + "]" 
						+ ", pricerange/vol=["
						+ minStockPrice + "-" + maxStockPrice + "," + minVolume
						+ "]", result);
		return result;
	}
	
	
	/**
	 * {@inheritDoc}
	 */
	@Override
	@Transactional
	public HashMap<String, Double> getPivotPoints(int pivotType, Stock s, int timeFrame){
		
		HashMap<String, Double> pivotPointsHashMap = null;
		double p, s1, s2, s3, r1, r2, r3;

		if(pivotType == LNSystemConstants.FIBNOCCI_PIVOT) {
			/*
		 	Pivot Point (P) = (High + Low + Close)/3
			Support 1 (S1) = P - {.382 * (High  -  Low)}
			Support 2 (S2) = P - {.618 * (High  -  Low)}
			Support 3 (S3) = P - {1 * (High  -  Low)}
			Resistance 1 (R1) = P + {.382 * (High  -  Low)}
			Resistance 2 (R2) = P + {.618 * (High  -  Low)}
			Resistance 3 (R3) = P + {1 * (High  -  Low)}
		 */
			//TODO - see if this can be combined into 1 query
			double high = stockDao.getHighForPivotCalculation(s, null, timeFrame);
			double low = stockDao.getLowForPivotCalculation(s, null, timeFrame);
			double close = stockDao.getCloseForPivotCalculation(s, null, timeFrame);
			
			//pivot
			p = (high+low+close)/3;
			s1 = p - (0.382 * (high - low));
			s2 = p - (0.618 * (high - low));
			s3 = p - (1 * (high - low));
	
			r1 = p + (0.382 * (high - low));
			r2 = p + (0.618 * (high - low));
			r3 = p + (1 * (high - low));
			
			pivotPointsHashMap = new HashMap<String, Double>();
			pivotPointsHashMap.put("s1", s1);
			pivotPointsHashMap.put("s2", s2);
			pivotPointsHashMap.put("s3", s3);
			pivotPointsHashMap.put("r1", r1);
			pivotPointsHashMap.put("r2", r2);
			pivotPointsHashMap.put("r3", r3);
			pivotPointsHashMap.put("p", p);
		}
		return pivotPointsHashMap;
		
	}
	
	/**
	 * {@inheritDoc}
	 */
	@Override
	@Transactional
	public PricePositionToPivots getPricePositionToPivots(Stock s, int pivotType, int timeFrame) {
		StringBuilder result = new StringBuilder("");
		double p, s1, s2, s3, r1, r2, r3;

		PricePositionToPivots pricePositionToPivots = new PricePositionToPivots();
		
		HashMap<String, Double> pivots = getPivotPoints(LNSystemConstants.FIBNOCCI_PIVOT, s, LNSystemConstants.DAILY_TIMEFRAME);
		p = pivots.get("p");
		s3 = pivots.get("s3");
		s2 = pivots.get("s2");
		s1 = pivots.get("s1");
		r1 = pivots.get("r1");
		r2 = pivots.get("r2");
		r3 = pivots.get("r3");
		
		logger.info(">>>PIVOTS:>>>>"+pivots);
		
			double lastClose = stockDao.getMostRecentClose(s);
			if(lastClose >= 0) {
				
				if(lastClose<s3) {
					result.append("<S3");
				} else if(lastClose>=s3 && lastClose<s2) {
					result.append(">=S3 <S2");
					getRiskRewardPrice(s3, lastClose, s2,pricePositionToPivots);
				} else if(lastClose>=s2 && lastClose<s1) {
					result.append(">=S2 <S1");
					getRiskRewardPrice(s2, lastClose, s1,pricePositionToPivots);
				} else if(lastClose>=s1 && lastClose<p) {
					result.append(">=S1 <P");
					getRiskRewardPrice(s1, lastClose, p,pricePositionToPivots);
				} else if(lastClose>=p && lastClose<r1) {
					result.append(">=P <R1");
					getRiskRewardPrice(p, lastClose, r1,pricePositionToPivots);
				} else if(lastClose>=r1 && lastClose<r2) {
					result.append(">=R1 <R2");
					getRiskRewardPrice(r1, lastClose, r2,pricePositionToPivots);
				} else if(lastClose>=r2 && lastClose<r3) {
					result.append(">=R2 <R3");
					getRiskRewardPrice(r2, lastClose, r3,pricePositionToPivots);
				} else if(lastClose>=r3) {
					result.append(">=R3");
				}
				

				pricePositionToPivots.setPricePosition(result.toString());

		}
		
		return pricePositionToPivots;
	}
	
	public void getRiskRewardPrice(double low, double current, double high,PricePositionToPivots pricePositionToPivots) {
		double risk = current-low;
		double reward = high-current;
		
		pricePositionToPivots.setRisk(risk);
		pricePositionToPivots.setReward(reward);
		pricePositionToPivots.setRiskRewardRatio(reward/risk);
		
		//return "[Risk="+((Math.round(current-low)*100)/100)+"/Reward:"+((Math.round(high-current)*100)/100)+"]";
		//return "[Risk="+(current-low)+"/Reward:"+(high-current)+"]";
	}
	
	@Override
	@Transactional
	public void addAnalyticsEvent(Stock s, StockDailyData todaysData, String analyticsEventType){
		
		// add an entry into analyticsevent table.
		//first get existing pocket pivot event (if any) for the stockdaily and add to details field.
		AnalyticsEvent event = analyticsEventDao.getAnalyticsEventsByTypeForADailyData(todaysData,"POCKET_PIVOT");
		
		if(event == null) {
			event = new AnalyticsEvent();
		} else {
			logger.info(">>> AnalyticsEvent table already has a "+analyticsEventType+" entry for :"+s.getSymbol()+" on ["+todaysData.getDate()+"]");
		}		
		
		StringBuilder sBuilder = new StringBuilder("");
		sBuilder.append(" -- Pocket pivot found on ["+todaysData.getDate()+"]--");
		//calculate pivots and add more details based on current price vs pivots
		// also set rank based on the pivots.
		
		event.setEventType(analyticsEventType);
		event.setDetails(sBuilder.toString());
		event.setSymbol(s.getSymbol());
		event.setCreatedOn(LocalDate.now().toDate());
		
		event.setStockId(s);
		event.setStockDailyId(todaysData);
		
		
		PricePositionToPivots pricePositionToPivots = 
				getPricePositionToPivots(s, LNSystemConstants.FIBNOCCI_PIVOT, LNSystemConstants.DAILY_TIMEFRAME);
		
		event.setPricePivotPosition(pricePositionToPivots.getPricePosition());
		event.setRisk(pricePositionToPivots.getRisk());
		event.setReward(pricePositionToPivots.getReward());
		event.setRiskReward(pricePositionToPivots.getRiskRewardRatio());
		
		event.setPriceOnEvent(todaysData.getClose());
		
		//persist event information.
		analyticsEventDao.saveAnalyticsEvent(event);
		
		
	}
	
	
	
	public class PricePositionToPivots {
		String pricePosition;
		double risk;
		double reward;
		double riskRewardRatio;
		
		public String getPricePosition() {
			return pricePosition;
		}
		public void setPricePosition(String pricePosition) {
			this.pricePosition = pricePosition;
		}
		public double getRisk() {
			return risk;
		}
		public void setRisk(double risk) {
			this.risk = risk;
		}
		public double getReward() {
			return reward;
		}
		public void setReward(double reward) {
			this.reward = reward;
		}
		public double getRiskRewardRatio() {
			return riskRewardRatio;
		}
		public void setRiskRewardRatio(double riskRewardRatio) {
			this.riskRewardRatio = riskRewardRatio;
		}
		
	}
}